using System;

namespace Atomic.Libraries.Mathematics.RandomNumbers
{
	[Serializable]
	public sealed class MersenneTwister : IRandomGenerator
	{
		private int index;
		private uint[] buffer;

		public MersenneTwister()
            : this(5489u)
		{
		}

		public MersenneTwister(uint seed)
		{
			buffer = new uint[624];
			buffer[0] = seed & 0xffffffffu;

			index = 1;
			do
			{
				buffer[index] = 1812433253u * (buffer[index - 1] ^ (buffer[index - 1] >> 30)) + (uint)index;
			} while (++index < 624);

			// Now index = 624.
		}

		public int Next()
		{
			return NextInt31();
		}

		public int Next(int maxValue)
		{
			return Next(0, maxValue);
		}
		
		public int Next(int minValue, int maxValue)
		{
			throw new NotImplementedException();
		}

		/// <summary>
		/// Returns a random number in the range from 0 to uint.MaxValue.
		/// </summary>
		public uint NextInt32()
		{
			uint s;

			if (index == 624)
			{
				int i = 0;
               
				do
				{
					s = (buffer[i] & 0x80000000u) | (buffer[i + 1] & 0x7fffffffu);
					buffer[i] = buffer[i + 397] ^ (s >> 1) ^ ((s & 0x00000001u) * 0x9908b0dfu);
				} while (++i < 227);

				do
				{
					s = (buffer[i] & 0x80000000u) | (buffer[i + 1] & 0x7fffffffu);
					buffer[i] = buffer[i - 227] ^ (s >> 1) ^ ((s & 0x00000001u) * 0x9908b0dfu);
				} while (++i < 623);
                
				s = (buffer[623] & 0x80000000u) | (buffer[0] & 0x7fffffffu);
				buffer[623] = buffer[396] ^ (s >> 1) ^ ((s & 0x00000001u) * 0x9908b0dfu);

				index = 0;
			}

			s = buffer[index++];

			s ^= (s >> 11);
			s ^= (s << 7) & 0x9d2c5680u;
			s ^= (s << 15) & 0xefc60000u;
			s ^= (s >> 18);

			return s;
		}

		/// <summary>
		/// Returns a random number in the range from 0 to int.MaxValue.
		/// </summary>
		public int NextInt31()
		{
			return (int)(NextInt32() >> 1);
		}

		/// <summary>
		/// Returns a random double-precision floating point number greater than or equal to 0.0, and less than 1.0.
		/// </summary>
		public double NextDouble()
		{
			return NextDouble32();
		}

		public double NextDouble32()
		{
			return NextInt32() * 2.3283064365386963e-10;
		}

		public double NextDouble53()
		{
			uint a = (NextInt32() >> 5);
			uint b = (NextInt32() >> 6);
			return (a * 67108864.0 + b) * 1.1102230246251565e-16;
		}

		public static bool Test()
		{
			// Compare with numbers generated by the implementation at http://www-personal.umich.edu/~wagnerr/MersenneTwister.html.

			MersenneTwister mt = new MersenneTwister(5489u);

			uint[] s = new uint[] { 725333953u, 637783061u, 3999684213u, 3080076094u, 2162234361u, 3451654520u, 3339750609u, 3462276555u, 1417232878u, 2529021949u };
			for (int i = 0; i < s.Length; i++)
			{
				for (int j = 0; j < 10000; j++)
				{
					mt.NextInt32();
				}
				if (s[i] != mt.NextInt32())
				{
					return false;
				}
			}

			return true;
		}
	}
}
